รักษาความปลอดภัย API ของ Django REST Framework ของคุณด้วยการพิสูจน์ตัวตนที่แข็งแกร่ง เปรียบเทียบการพิสูจน์ตัวตนด้วย Token และการใช้งาน JWT (JSON Web Token) รวมถึงตัวอย่างโค้ดและแนวทางปฏิบัติที่ดีที่สุด
การพิสูจน์ตัวตน Python DRF: การใช้งาน Token เทียบกับ JWT สำหรับ API ที่แข็งแกร่ง
การรักษาความปลอดภัย API ของคุณเป็นสิ่งสำคัญยิ่ง เมื่อสร้าง API ด้วย Python และ Django REST Framework (DRF) คุณมีตัวเลือกการพิสูจน์ตัวตนหลายแบบ บทความนี้เจาะลึกวิธีการยอดนิยมสองวิธี: การพิสูจน์ตัวตนด้วย Token และการพิสูจน์ตัวตนด้วย JWT (JSON Web Token) เปรียบเทียบจุดแข็งและจุดอ่อน และให้ตัวอย่างการใช้งานจริง
ทำความเข้าใจกับการพิสูจน์ตัวตนใน API
การพิสูจน์ตัวตนคือกระบวนการตรวจสอบข้อมูลประจำตัวของผู้ใช้หรือแอปพลิเคชันที่เข้าถึง API ของคุณ ระบบการพิสูจน์ตัวตนที่ใช้งานได้ดีจะช่วยให้มั่นใจได้ว่าเฉพาะหน่วยงานที่ได้รับอนุญาตเท่านั้นที่สามารถเข้าถึงทรัพยากรที่ได้รับการป้องกัน ในบริบทของ RESTful API การพิสูจน์ตัวตนโดยทั่วไปเกี่ยวข้องกับการส่งข้อมูลรับรอง (เช่น ชื่อผู้ใช้และรหัสผ่าน) พร้อมกับแต่ละคำขอ จากนั้นเซิร์ฟเวอร์จะตรวจสอบข้อมูลรับรองเหล่านี้ และหากถูกต้อง จะให้สิทธิ์การเข้าถึง
การพิสูจน์ตัวตนด้วย Token
การพิสูจน์ตัวตนด้วย Token เป็นกลไกที่เรียบง่ายและตรงไปตรงมา เมื่อผู้ใช้เข้าสู่ระบบสำเร็จ เซิร์ฟเวอร์จะสร้างโทเค็นแบบสุ่มที่ไม่ซ้ำกันและจัดเก็บไว้ในฐานข้อมูล โดยเชื่อมโยงกับผู้ใช้ จากนั้นไคลเอนต์จะส่งโทเค็นนี้ในส่วนหัว 'Authorization' ของคำขอที่ตามมา เซิร์ฟเวอร์จะดึงโทเค็นจากฐานข้อมูล ตรวจสอบความถูกต้อง และให้สิทธิ์การเข้าถึงตามนั้น
การใช้งานกับ DRF
DRF ให้การสนับสนุนในตัวสำหรับการพิสูจน์ตัวตนด้วย Token นี่คือวิธีการใช้งาน:
- ติดตั้ง DRF และลงทะเบียนในโครงการ Django ของคุณ:
ขั้นแรก ตรวจสอบให้แน่ใจว่าคุณได้ติดตั้ง Django REST Framework แล้ว:
pip install djangorestframework
จากนั้น เพิ่มลงใน `INSTALLED_APPS` ใน `settings.py`:
INSTALLED_APPS = [
...
'rest_framework',
]
- เพิ่มรูปแบบ TokenAuthentication เป็นคลาสการพิสูจน์ตัวตนเริ่มต้น (ไม่บังคับ แต่แนะนำ):
ในไฟล์ `settings.py` ของคุณ ให้เพิ่มสิ่งต่อไปนี้:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
],
}
ซึ่งจะใช้ Token Authentication ทั่วทั้ง API ของคุณ `SessionAuthentication` รวมอยู่ด้วยสำหรับการโต้ตอบผ่านเบราว์เซอร์ แต่คุณสามารถลบออกได้สำหรับแอปพลิเคชันที่ขับเคลื่อนด้วย API อย่างหมดจด
- สร้าง Token สำหรับผู้ใช้แต่ละคน:
คุณสามารถสร้างโทเค็นสำหรับผู้ใช้โดยอัตโนมัติเมื่อสร้างโดยเพิ่มตัวจัดการสัญญาณ สร้างไฟล์ชื่อ `signals.py` ในแอปของคุณ (เช่น `users/signals.py`):
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token
@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
if created:
Token.objects.create(user=instance)
จากนั้น นำเข้าไฟล์ `signals.py` นี้ในไฟล์ `users/apps.py` ของคุณภายในเมธอด `ready` ของคลาสการกำหนดค่าแอปของคุณ ตัวอย่างสำหรับ `users/apps.py`:
from django.apps import AppConfig
class UsersConfig(AppConfig):
default_auto_field = 'django.db.BigAutoField'
name = 'users'
def ready(self):
import users.signals
ตอนนี้คุณสามารถจัดการ tokes โดยใช้บรรทัดคำสั่ง:
python manage.py drf_create_token <username>
- ใช้งานมุมมอง API ของคุณ:
นี่คือตัวอย่างง่ายๆ ของมุมมองที่ต้องใช้การพิสูจน์ตัวตนด้วย Token:
from rest_framework import permissions
from rest_framework.response import Response
from rest_framework.views import APIView
class ExampleView(APIView):
authentication_classes = [TokenAuthentication]
permission_classes = [permissions.IsAuthenticated]
def get(self, request):
content = {
'message': 'Hello, ' + request.user.username + '! You are authenticated.',
}
return Response(content)
ในตัวอย่างนี้ `authentication_classes` ระบุว่าควรใช้การพิสูจน์ตัวตนด้วย Token และ `permission_classes` ระบุว่าเฉพาะผู้ใช้ที่ได้รับการรับรองความถูกต้องเท่านั้นที่สามารถเข้าถึงมุมมอง
- รวมมุมมอง API เข้าสู่ระบบ:
คุณยังต้องมีปลายทางเพื่อสร้างโทเค็นเมื่อเข้าสู่ระบบสำเร็จ:
from django.contrib.auth import authenticate
from rest_framework import status
from rest_framework.authtoken.models import Token
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
@api_view(['POST'])
@permission_classes([AllowAny])
def login(request):
username = request.data.get('username')
password = request.data.get('password')
user = authenticate(username=username, password=password)
if user:
token, _ = Token.objects.get_or_create(user=user)
return Response({'token': token.key})
else:
return Response({'error': 'Invalid Credentials'}, status=status.HTTP_401_UNAUTHORIZED)
ข้อดีของการพิสูจน์ตัวตนด้วย Token
- ความเรียบง่าย: ง่ายต่อการใช้งานและทำความเข้าใจ
- Stateless: คำขอโทเค็นแต่ละรายการมีข้อมูลที่ช่วยให้สามารถทำงานได้ด้วยตัวเอง
ข้อเสียของการพิสูจน์ตัวตนด้วย Token
- การพึ่งพาฐานข้อมูล: ต้องมีการค้นหาฐานข้อมูลสำหรับแต่ละคำขอเพื่อตรวจสอบความถูกต้องของโทเค็น ซึ่งอาจส่งผลต่อประสิทธิภาพ โดยเฉพาะอย่างยิ่งในระดับ
- การเพิกถอนโทเค็น: การเพิกถอนโทเค็นต้องลบออกจากฐานข้อมูล ซึ่งอาจซับซ้อนได้
- ความสามารถในการปรับขนาด: อาจไม่ใช่โซลูชันที่ปรับขนาดได้มากที่สุดสำหรับ API ขนาดใหญ่ที่มีการรับส่งข้อมูลสูงเนื่องจากค่าใช้จ่ายของฐานข้อมูล
การพิสูจน์ตัวตนด้วย JWT (JSON Web Token)
การพิสูจน์ตัวตนด้วย JWT เป็นแนวทางที่ทันสมัยและซับซ้อนกว่า JWT คืออ็อบเจ็กต์ JSON ที่กะทัดรัดและปลอดภัย URL ซึ่งมีข้อเรียกร้องเกี่ยวกับผู้ใช้ ข้อเรียกร้องเหล่านี้ได้รับการลงนามแบบดิจิทัลโดยใช้คีย์ลับหรือคู่คีย์สาธารณะ/ส่วนตัว เมื่อผู้ใช้เข้าสู่ระบบ เซิร์ฟเวอร์จะสร้าง JWT และส่งไปยังไคลเอนต์ จากนั้นไคลเอนต์จะรวม JWT นี้ในส่วนหัว 'Authorization' ของคำขอที่ตามมา เซิร์ฟเวอร์สามารถตรวจสอบลายเซ็นของ JWT ได้โดยไม่จำเป็นต้องเข้าถึงฐานข้อมูล ทำให้เป็นโซลูชันที่มีประสิทธิภาพและปรับขนาดได้มากกว่า
การใช้งานกับ DRF
DRF ไม่ได้ให้การสนับสนุนในตัวสำหรับการพิสูจน์ตัวตนด้วย JWT แต่ไลบรารีที่ยอดเยี่ยมหลายแห่งทำให้ง่ายต่อการผสานรวม หนึ่งในไลบรารีที่ได้รับความนิยมมากที่สุดคือ `djangorestframework-simplejwt`
- ติดตั้ง `djangorestframework-simplejwt`:
pip install djangorestframework-simplejwt
- กำหนดค่าการตั้งค่า DRF:
ในไฟล์ `settings.py` ของคุณ ให้เพิ่มสิ่งต่อไปนี้:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
}
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'ROTATE_REFRESH_TOKENS': False,
'BLACKLIST_AFTER_ROTATION': True,
'ALGORITHM': 'HS256',
'SIGNING_KEY': settings.SECRET_KEY,
'VERIFYING_KEY': None,
'AUTH_HEADER_TYPES': ('Bearer',),
'USER_ID_FIELD': 'id',
'USER_ID_CLAIM': 'user_id',
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
'TOKEN_TYPE_CLAIM': 'token_type',
}
คำอธิบายการตั้งค่า:
- `ACCESS_TOKEN_LIFETIME`: ระยะเวลาที่โทเค็นการเข้าถึงถูกต้อง (ตัวอย่าง 5 นาที)
- `REFRESH_TOKEN_LIFETIME`: ระยะเวลาที่โทเค็นการรีเฟรชถูกต้อง (ตัวอย่าง 1 วัน) โทเค็นการรีเฟรชใช้เพื่อรับโทเค็นการเข้าถึงใหม่โดยไม่จำเป็นต้องให้ผู้ใช้เข้าสู่ระบบอีกครั้ง
- `ROTATE_REFRESH_TOKENS`: เลือกว่าจะหมุนเวียนโทเค็นการรีเฟรชหลังการใช้งานแต่ละครั้งหรือไม่
- `BLACKLIST_AFTER_ROTATION`: เลือกว่าจะขึ้นบัญชีดำโทเค็นการรีเฟรชเก่าหลังจากหมุนเวียนหรือไม่
- `ALGORITHM`: อัลกอริทึมที่ใช้ในการลงนาม JWT (HS256 เป็นตัวเลือกทั่วไป)
- `SIGNING_KEY`: คีย์ลับที่ใช้ในการลงนาม JWT (โดยทั่วไปคือ Django SECRET_KEY ของคุณ)
- `AUTH_HEADER_TYPES`: ประเภทของส่วนหัวการอนุญาต (โดยทั่วไปคือ "Bearer")
- รวมมุมมอง API เข้าสู่ระบบและรีเฟรชโทเค็น:
`djangorestframework-simplejwt` มีมุมมองสำหรับการรับและรีเฟรชโทเค็น รวมไว้ใน `urls.py` ของคุณ:
from django.urls import path
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
)
urlpatterns = [
path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]
`TokenObtainPairView` ให้โทเค็นการเข้าถึงและการรีเฟรชหลังจากการพิสูจน์ตัวตนสำเร็จ `TokenRefreshView` ให้โทเค็นการเข้าถึงใหม่เมื่อได้รับโทเค็นการรีเฟรชที่ถูกต้อง
- ใช้งานมุมมอง API ของคุณ:
นี่คือตัวอย่างง่ายๆ ของมุมมองที่ต้องใช้การพิสูจน์ตัวตนด้วย JWT:
from rest_framework import permissions
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_simplejwt.authentication import JWTAuthentication
class ExampleView(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [permissions.IsAuthenticated]
def get(self, request):
content = {
'message': 'Hello, ' + request.user.username + '! You are authenticated.',
}
return Response(content)
เช่นเดียวกับตัวอย่างการพิสูจน์ตัวตนด้วย Token `authentication_classes` ระบุว่าควรใช้การพิสูจน์ตัวตนด้วย JWT และ `permission_classes` จำกัดการเข้าถึงเฉพาะผู้ใช้ที่ได้รับการรับรองความถูกต้องเท่านั้น
ข้อดีของการพิสูจน์ตัวตนด้วย JWT
- ความสามารถในการปรับขนาด: ไม่จำเป็นต้องมีการค้นหาฐานข้อมูลเพื่อตรวจสอบความถูกต้องของโทเค็น ทำให้ปรับขนาดได้มากขึ้น
- Stateless: JWT มีข้อมูลที่จำเป็นทั้งหมดสำหรับการพิสูจน์ตัวตน
- ได้มาตรฐาน: JWT เป็นมาตรฐานที่ได้รับการยอมรับอย่างกว้างขวาง ซึ่งได้รับการสนับสนุนโดยไลบรารีและแพลตฟอร์มมากมาย
- Microservices Friendly: เหมาะสำหรับสถาปัตยกรรม microservices เนื่องจากบริการต่างๆ สามารถตรวจสอบ JWT ได้อย่างอิสระ
ข้อเสียของการพิสูจน์ตัวตนด้วย JWT
- ความซับซ้อน: ซับซ้อนกว่าในการใช้งานมากกว่าการพิสูจน์ตัวตนด้วย Token
- ขนาดโทเค็น: JWT อาจมีขนาดใหญ่กว่าโทเค็นธรรมดา ซึ่งอาจเพิ่มการใช้แบนด์วิธ
- การเพิกถอนโทเค็น: การเพิกถอน JWT เป็นเรื่องท้าทาย เมื่อออกแล้ว จะใช้ได้จนกว่าจะหมดอายุ วิธีแก้ปัญหาเกี่ยวข้องกับการขึ้นบัญชีดำโทเค็นที่ถูกเพิกถอน ซึ่งจะนำการพึ่งพาฐานข้อมูลกลับมาอีกครั้ง
กลยุทธ์การเพิกถอนโทเค็น
ทั้งวิธีการพิสูจน์ตัวตนด้วย Token และ JWT ต้องมีกลไกสำหรับการเพิกถอนการเข้าถึง นี่คือวิธีที่คุณสามารถเข้าถึงการเพิกถอนโทเค็น:
การเพิกถอนการพิสูจน์ตัวตนด้วย Token
ด้วยการพิสูจน์ตัวตนด้วย Token การเพิกถอนเป็นเรื่องตรงไปตรงมา: เพียงแค่ลบโทเค็นออกจากฐานข้อมูล:
from rest_framework.authtoken.models import Token
try:
token = Token.objects.get(user=request.user)
token.delete()
except Token.DoesNotExist:
pass
การเพิกถอนการพิสูจน์ตัวตนด้วย JWT
การเพิกถอน JWT มีความซับซ้อนมากขึ้นเนื่องจากโทเค็นนั้นมีอยู่ในตัวเองและไม่ได้อาศัยการค้นหาฐานข้อมูลเพื่อตรวจสอบความถูกต้อง (ในตอนแรก) กลยุทธ์ทั่วไป ได้แก่:
- การขึ้นบัญชีดำโทเค็น: จัดเก็บโทเค็นที่ถูกเพิกถอนไว้ในบัญชีดำ (เช่น ตารางฐานข้อมูลหรือแคช Redis) ก่อนที่จะตรวจสอบความถูกต้องของ JWT ให้ตรวจสอบว่าอยู่ในบัญชีดำหรือไม่ `djangorestframework-simplejwt` ให้การสนับสนุนในตัวสำหรับการขึ้นบัญชีดำโทเค็นการรีเฟรช
- เวลาหมดอายุสั้น: ใช้เวลาหมดอายุของโทเค็นการเข้าถึงสั้นๆ และอาศัยโทเค็นการรีเฟรชเพื่อรับโทเค็นการเข้าถึงใหม่บ่อยๆ ซึ่งจะจำกัดโอกาสที่โทเค็นที่ถูกบุกรุกจะถูกใช้งาน
- หมุนเวียนโทเค็นการรีเฟรช: หมุนเวียนโทเค็นการรีเฟรชหลังจากการใช้งานแต่ละครั้ง ซึ่งจะทำให้โทเค็นเก่าไม่ถูกต้องในแต่ละครั้งและป้องกันการขโมยโทเค็น
OAuth2 และ OpenID Connect
สำหรับสถานการณ์การพิสูจน์ตัวตนและการอนุญาตที่ซับซ้อนมากขึ้น ให้พิจารณาใช้ OAuth2 และ OpenID Connect มาตรฐานเหล่านี้มีกรอบการทำงานที่แข็งแกร่งสำหรับการมอบหมายการเข้าถึงทรัพยากรโดยไม่ต้องแชร์ข้อมูลรับรอง OAuth2 เป็นโปรโตคอลการอนุญาตเป็นหลัก ในขณะที่ OpenID Connect สร้างขึ้นบน OAuth2 เพื่อให้บริการพิสูจน์ตัวตน แพ็กเกจ Django หลายแพ็กเกจ เช่น `django-oauth-toolkit` และ `django-allauth` ช่วยอำนวยความสะดวกในการรวม OAuth2 และ OpenID Connect เข้ากับ DRF API ของคุณ
สถานการณ์ตัวอย่าง: ผู้ใช้ต้องการให้แอปพลิเคชันของบุคคลที่สามเข้าถึงข้อมูลของตนที่จัดเก็บไว้ใน API ของคุณ ด้วย OAuth2 ผู้ใช้สามารถให้สิทธิ์แอปพลิเคชันโดยไม่ต้องแชร์ชื่อผู้ใช้และรหัสผ่านของตน แทนที่จะเป็นเช่นนั้น แอปพลิเคชันจะได้รับโทเค็นการเข้าถึงที่สามารถใช้เพื่อเข้าถึงข้อมูลของผู้ใช้ภายในขอบเขตสิทธิ์ที่กำหนด
การเลือกวิธีการพิสูจน์ตัวตนที่เหมาะสม
วิธีการพิสูจน์ตัวตนที่ดีที่สุดขึ้นอยู่กับข้อกำหนดเฉพาะของคุณ:
- ความเรียบง่ายและความเร็วในการใช้งาน: โดยทั่วไปแล้ว การพิสูจน์ตัวตนด้วย Token จะง่ายกว่าในการใช้งานในขั้นต้น
- ความสามารถในการปรับขนาด: การพิสูจน์ตัวตนด้วย JWT สามารถปรับขนาดได้มากกว่าสำหรับ API ที่มีการรับส่งข้อมูลสูง
- ข้อกำหนดด้านความปลอดภัย: พิจารณาความละเอียดอ่อนของข้อมูลของคุณและระดับความปลอดภัยที่จำเป็น OAuth2/OpenID Connect นำเสนอคุณสมบัติด้านความปลอดภัยที่แข็งแกร่งที่สุด แต่ต้องมีการใช้งานที่ซับซ้อนกว่า
- สถาปัตยกรรม Microservices: JWT เหมาะสมอย่างยิ่งสำหรับ microservices เนื่องจากแต่ละบริการสามารถตรวจสอบโทเค็นได้อย่างอิสระ
แนวทางปฏิบัติที่ดีที่สุดสำหรับการพิสูจน์ตัวตน API
- ใช้ HTTPS: ใช้ HTTPS เสมอเพื่อเข้ารหัสการสื่อสารระหว่างไคลเอนต์และเซิร์ฟเวอร์ ปกป้องข้อมูลรับรองจากการดักฟัง
- จัดเก็บความลับอย่างปลอดภัย: อย่าจัดเก็บคีย์ลับหรือรหัสผ่านในรูปแบบข้อความธรรมดา ใช้ตัวแปรสภาพแวดล้อมหรือเครื่องมือการจัดการการกำหนดค่าที่ปลอดภัย
- ใช้งานการจำกัดอัตรา: ปกป้อง API ของคุณจากการใช้งานในทางที่ผิดโดยการใช้งานการจำกัดอัตราเพื่อจำกัดจำนวนคำขอที่ไคลเอนต์สามารถทำได้ภายในช่วงเวลาที่กำหนด
- ตรวจสอบความถูกต้องของอินพุต: ตรวจสอบความถูกต้องของข้อมูลอินพุตทั้งหมดอย่างละเอียดเพื่อป้องกันการโจมตีแบบฉีด
- ตรวจสอบและบันทึก: ตรวจสอบ API ของคุณเพื่อหากิจกรรมที่น่าสงสัยและบันทึกเหตุการณ์การพิสูจน์ตัวตนเพื่อวัตถุประสงค์ในการตรวจสอบ
- อัปเดตไลบรารีเป็นประจำ: ทำให้ Django, DRF และไลบรารีการพิสูจน์ตัวตนของคุณเป็นปัจจุบันอยู่เสมอเพื่อรับประโยชน์จากแพตช์ความปลอดภัยและการปรับปรุง
- ใช้งาน CORS (การแชร์ทรัพยากรข้ามต้นทาง): กำหนดค่า CORS อย่างถูกต้องเพื่อให้เฉพาะโดเมนที่เชื่อถือได้เท่านั้นที่สามารถเข้าถึง API ของคุณจากเว็บเบราว์เซอร์
บทสรุป
การเลือกวิธีการพิสูจน์ตัวตนที่เหมาะสมเป็นสิ่งสำคัญสำหรับการรักษาความปลอดภัย DRF API ของคุณ การพิสูจน์ตัวตนด้วย Token นำเสนอความเรียบง่าย ในขณะที่การพิสูจน์ตัวตนด้วย JWT ให้ความสามารถในการปรับขนาดและความยืดหยุ่น การทำความเข้าใจข้อดีและข้อเสียของแต่ละวิธี พร้อมด้วยแนวทางปฏิบัติที่ดีที่สุดสำหรับความปลอดภัยของ API จะช่วยให้คุณสร้าง API ที่แข็งแกร่งและปลอดภัยซึ่งปกป้องข้อมูลและผู้ใช้ของคุณ
อย่าลืมพิจารณาความต้องการเฉพาะของคุณและเลือกโซลูชันที่สร้างสมดุลที่ดีที่สุดระหว่างความปลอดภัย ประสิทธิภาพ และความง่ายในการใช้งาน สำรวจ OAuth2 และ OpenID Connect สำหรับสถานการณ์การอนุญาตที่ซับซ้อนมากขึ้น